package com.szeldon.declatest.reports;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;

import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.ParsingException;
import nu.xom.ValidityException;

import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.szeldon.declatest.declaration.TestsSuiteDeclaration;
import com.szeldon.declatest.exec.TestSuiteExecHistory;
import com.szeldon.declatest.exec.events.IfConditionEvaluationEndExecEvent;
import com.szeldon.declatest.exec.events.IfConditionEvaluationStartExecEvent;
import com.szeldon.declatest.exec.events.ScenarioExecutionEndExecEvent;
import com.szeldon.declatest.exec.events.ScenarioExecutionStartExecEvent;
import com.szeldon.declatest.exec.events.dostep.DoScenarioStepEndExecEvent;
import com.szeldon.declatest.exec.events.dostep.DoScenarioStepStartExecEvent;
import com.szeldon.declatest.exec.events.dostep.jms.JmsMessageReceivedExecEvent;
import com.szeldon.declatest.exec.events.dostep.jms.JmsMessageSentExecEvent;
import com.szeldon.declatest.exec.result.EndScenarioExecutionResult;
import com.szeldon.declatest.reports.exceptions.GeneratingReportException;
import com.szeldon.utils.ValuesUtils;

/**
 * Tests for generator of test reports. Plain testing of generating XML reports, plugins are tested in their own
 * classes.
 * 
 * @author szeldon
 * 
 */
public class ReportGeneratorTest {

    public static final String TESTS_RESOURCES_DIRECTORY = "tests-res/";
    public static final String TEST_REPORT_XML_DATA_OUTPUT_DIRECTORY = "output/reports/xml/";
    public static final String TEST_REPORT_PLUGINS_OUTPUT_DIRECTORY = "output/reports/plugins/";
    public static final String TESTS_REQUESTS_RESPONSES_DIRECTORY = "com/szeldon/declatest/reports/";
    public static final String REPORT_TAG_FOR_TEST_SUITE = "TestSuite";

    private ReportGenerator generator = null;
    private TestsSuiteDeclaration suite = null;
    private TestSuiteExecHistory history = null;
    private Properties properties = null;
    private String testSuiteId;
    private String testSuiteDescription;

    @BeforeTest
    public void beforeTest() throws IOException {
        // inits attributes for generator
        testSuiteId = "testSuiteId";
        testSuiteDescription = "testSuiteDescription";
        suite = new TestsSuiteDeclaration();
        suite.addTestSuiteAttribute("id", testSuiteId);
        suite.addTestSuiteAttribute("description", testSuiteDescription);
        history = new TestSuiteExecHistory(testSuiteId);
        properties = new Properties();

        // creates and uses generator
        generator = new ReportGenerator();
    }

    @Test
    public void emptyTestSuiteTest() throws IOException, ValidityException, ParsingException, GeneratingReportException {

        generator.generateReport(suite, history, properties, TEST_REPORT_XML_DATA_OUTPUT_DIRECTORY,
                TEST_REPORT_PLUGINS_OUTPUT_DIRECTORY);

        // checks if there's a simple empty tag for test suite and nothing else
        File file = checkIfOneXmlExists();
        Document document = readFileIntoDocument(file);
        Element root = document.getRootElement();
        Assert.assertEquals(root.getLocalName(), REPORT_TAG_FOR_TEST_SUITE);
        Assert.assertEquals(root.getAttributeCount(), 2);
        Assert.assertEquals(root.getAttributeValue("id"), testSuiteId);
        Assert.assertEquals(root.getAttributeValue("description"), testSuiteDescription);
        Assert.assertEquals(root.getChildCount(), 0);

        // cleans after itself... deletes report
        file.delete();
    }

    @Test
    public void oneSimpleIfScenarioTest() throws IOException, ValidityException, ParsingException,
            GeneratingReportException {
        String scenarioId = "scenarioId";
        String someEventDescription = "someEventDescription";
        String stepId = "stepId";
        String stepDescription = "stepDescription";
        String stepEndpointId = "stepEndpointId";
        String stepRequestPath = TESTS_RESOURCES_DIRECTORY + TESTS_REQUESTS_RESPONSES_DIRECTORY + "request01.xml";
        String stepResponsePath = TESTS_RESOURCES_DIRECTORY + TESTS_REQUESTS_RESPONSES_DIRECTORY + "response01.xml";
        String endpointDescription = "endpointDescription";
        String endpointUrl = "example.com";
        String endpointSettingsId = "endpointSettingsId";
        String left = "leftPart";
        String operator = "operator";
        String right = "rightPart";
        String descriptionForIf = "descriptionForIf";
        String leftEvaluated = "leftEvaluatedPart";
        String rightEvaluated = "rightEvaluatedPart";
        boolean isEvaluated = true;
        history.addExecEvent(new ScenarioExecutionStartExecEvent(scenarioId, someEventDescription, new Date()));
        history.addExecEvent(new IfConditionEvaluationStartExecEvent(scenarioId, someEventDescription, new Date(),
                left, operator, right, descriptionForIf));
        history.addExecEvent(new IfConditionEvaluationEndExecEvent(scenarioId, someEventDescription, new Date(),
                leftEvaluated, operator, rightEvaluated, descriptionForIf, isEvaluated));
        history.addExecEvent(new DoScenarioStepStartExecEvent(scenarioId, someEventDescription, new Date(), stepId,
                stepDescription, stepEndpointId, stepRequestPath, endpointDescription, endpointUrl, endpointSettingsId));
        history.addExecEvent(new JmsMessageSentExecEvent(scenarioId, someEventDescription, new Date(), stepId,
                "stepDescription", "stepEndpointId", "stepRequestPath", "endpointDescription", "endpointUrl",
                "endpointSettingsId", "queue", "queueName", getFileContent(stepRequestPath)));
        history.addExecEvent(new JmsMessageReceivedExecEvent(scenarioId, stepId, new Date(), someEventDescription,
                "stepDescription", "stepEndpointId", "stepRequestPath", "endpointDescription", "endpointUrl",
                "endpointSettingsId", "queue", "queueName", getFileContent(stepResponsePath)));
        history.addExecEvent(new DoScenarioStepEndExecEvent(scenarioId, someEventDescription, new Date(), stepId,
                stepDescription, stepEndpointId, stepRequestPath, endpointDescription, endpointUrl, endpointSettingsId));
        history.addExecEvent(new ScenarioExecutionEndExecEvent(scenarioId, someEventDescription, new Date(),
                new EndScenarioExecutionResult(null)));
        generator.generateReport(suite, history, properties, TEST_REPORT_XML_DATA_OUTPUT_DIRECTORY,
                TEST_REPORT_PLUGINS_OUTPUT_DIRECTORY);

        // checking if test suite element is in report
        File file = checkIfOneXmlExists();
        Document document = readFileIntoDocument(file);
        Element root = document.getRootElement();
        Assert.assertEquals(root.getLocalName(), REPORT_TAG_FOR_TEST_SUITE);
        Assert.assertEquals(root.getAttributeCount(), 2);
        Assert.assertEquals(root.getAttributeValue("id"), testSuiteId);
        Assert.assertEquals(root.getAttributeValue("description"), testSuiteDescription);
        Assert.assertEquals(root.getChildCount(), 1);

        // checking scenario
        Element scenariosElement = (Element) root.getChild(0);
        Assert.assertEquals(scenariosElement.getLocalName(), "Scenarios");
        Assert.assertEquals(scenariosElement.getChildCount(), 1);
        Element certainScenarioElement = (Element) scenariosElement.getChild(0);
        Assert.assertEquals(certainScenarioElement.getLocalName(), "Scenario");
        Assert.assertEquals(certainScenarioElement.getChildCount(), 8);

        // scenario start
        Element startElement = (Element) certainScenarioElement.getChild(0);
        Assert.assertEquals(startElement.getLocalName(), "ScenarioExecutionStartExecEvent");
        Assert.assertEquals(startElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(startElement.getAttributeValue("eventDate")), false);
        // if start
        Element ifStartElement = (Element) certainScenarioElement.getChild(1);
        Assert.assertEquals(ifStartElement.getLocalName(), "IfConditionEvaluationStartExecEvent");
        Assert.assertEquals(ifStartElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(ifStartElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(ifStartElement.getAttributeValue("left"), left);
        Assert.assertEquals(ifStartElement.getAttributeValue("operator"), operator);
        Assert.assertEquals(ifStartElement.getAttributeValue("right"), right);
        Assert.assertEquals(ifStartElement.getAttributeValue("ifDescription"), descriptionForIf);
        // if end
        Element ifEndElement = (Element) certainScenarioElement.getChild(2);
        Assert.assertEquals(ifEndElement.getLocalName(), "IfConditionEvaluationEndExecEvent");
        Assert.assertEquals(ifEndElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(ifEndElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(ifEndElement.getAttributeValue("evaluatedLeft"), leftEvaluated);
        Assert.assertEquals(ifEndElement.getAttributeValue("operator"), operator);
        Assert.assertEquals(ifEndElement.getAttributeValue("evaluatedRight"), rightEvaluated);
        Assert.assertEquals(ifEndElement.getAttributeValue("ifDescription"), descriptionForIf);
        // do start
        Element doStartElement = (Element) certainScenarioElement.getChild(3);
        Assert.assertEquals(doStartElement.getLocalName(), "DoScenarioStepStartExecEvent");
        Assert.assertEquals(doStartElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(doStartElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(doStartElement.getAttributeValue("endpointUrl"), endpointUrl);
        Assert.assertEquals(doStartElement.getAttributeValue("stepDescription"), stepDescription);
        Assert.assertEquals(doStartElement.getAttributeValue("requestPath"), stepRequestPath);
        // request
        Element requestElement = (Element) certainScenarioElement.getChild(4);
        Assert.assertEquals(requestElement.getLocalName(), "RequestSentExecEvent");
        Assert.assertEquals(requestElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(requestElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(requestElement.getAttributeValue("requestContent"), getFileContent(stepRequestPath));
        // response
        Element responseElement = (Element) certainScenarioElement.getChild(5);
        Assert.assertEquals(responseElement.getLocalName(), "ResponseReceivedExecEvent");
        Assert.assertEquals(responseElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(responseElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(responseElement.getAttributeValue("responseContent"), getFileContent(stepResponsePath));
        // do end
        Element doEndElement = (Element) certainScenarioElement.getChild(6);
        Assert.assertEquals(doEndElement.getLocalName(), "DoScenarioStepEndExecEvent");
        Assert.assertEquals(doEndElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(doEndElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(doEndElement.getAttributeValue("endpointUrl"), endpointUrl);
        Assert.assertEquals(doEndElement.getAttributeValue("stepDescription"), stepDescription);
        Assert.assertEquals(doEndElement.getAttributeValue("requestPath"), stepRequestPath);
        // scenario end
        Element scenarioEndElement = (Element) certainScenarioElement.getChild(7);
        Assert.assertEquals(scenarioEndElement.getLocalName(), "ScenarioExecutionEndExecEvent");
        Assert.assertEquals(scenarioEndElement.getAttributeValue("eventDescription"), someEventDescription);
        Assert.assertEquals(ValuesUtils.isNull(scenarioEndElement.getAttributeValue("eventDate")), false);
        Assert.assertEquals(scenarioEndElement.getAttributeValue("executionResult"), "end");

        // cleans after itself... deletes report
        file.delete();
    }

    public static File checkIfOneXmlExists() {
        return checkIfOneFileWithExtensionExistsInDirectory(".xml", TEST_REPORT_XML_DATA_OUTPUT_DIRECTORY);
    }

    /**
     * 
     * @param extension
     *            Extension in ".extension" format.
     * @param directory
     * @return
     */
    public static File checkIfOneFileWithExtensionExistsInDirectory(String extension, String directory) {
        File file = new File(directory);
        File[] files = file.listFiles();
        Assert.assertEquals(files.length, 1);
        file = files[0];
        Assert.assertEquals(file.getName().endsWith(extension), true);
        return file;
    }

    public static Document readFileIntoDocument(File file) throws ValidityException, ParsingException, IOException {
        Builder builder = new Builder();
        return builder.build(file);
    }

    /**
     * 
     * @param filePath
     * @return
     * @throws IOException
     */
    public static String getFileContent(String filePath) throws IOException {
        StringBuilder builder = new StringBuilder();
        File file = new File(filePath);
        String endOfLine = System.getProperty("line.separator");
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line = null;
        while ((line = reader.readLine()) != null) {
            builder.append(line + endOfLine);
        }

        return builder.toString();
    }
}
